package vocabularyhub.word.mw.process;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.Source;
import net.htmlparser.jericho.StartTag;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.log4j.Logger;

import vocabularyhub.word.impl.SimplestWord;
import vocabularyhub.word.model.IWord;
import vocabularyhub.word.mw.thesaurus.impl.ThesaurusItem;
import vocabularyhub.word.mw.thesaurus.impl.ThesaurusWord;
import vocabularyhub.word.mw.thesaurus.model.IConstants;
import vocabularyhub.word.mw.thesaurus.model.IThesaurusItem;
import vocabularyhub.word.mw.thesaurus.model.IThesaurusWord;
import vocabularyhub.word.mw.thesaurus.model.WordNet;
import vocabularyhub.word.process.util.SerializeUtil;

public class WordThesaurusProcessor implements IConstants {

    static Logger logger = Logger.getLogger(WordThesaurusProcessor.class);

    private static WordThesaurusProcessor processor;
    DefaultHttpClient httpclient;
    HttpGet httpget;

    private WordThesaurusProcessor() {
        httpclient = new DefaultHttpClient();
    }

    @Override
    public void finalize() {
        // When HttpClient instance is no longer needed,
        // shut down the connection manager to ensure
        // immediate deallocation of all system resources
        httpclient.getConnectionManager().shutdown();
    }

    public List<IWord> getWords(List<IWord> simpleWordList)
    throws ClientProtocolException, IOException {

        List<IWord> words = new LinkedList<IWord>();
        List<IWord> nonFoundWord = new LinkedList<IWord>();
        IWord word = null;
        int count = 0;
        for (IWord simpleWord : simpleWordList) {
            try {
                count++;
                word = getWord(simpleWord.getWord());
            } catch (NoHttpResponseException e) {
                try {
                    Thread.sleep(500);
                    word = getWord(simpleWord.getWord());
                } catch (InterruptedException e1) {
                } catch (NoHttpResponseException nhE) {
                    logger.warn(String.format(
                            "\"%s\" fail due to no http response", simpleWord));
                }

            } catch (UnknownHostException e) {
                logger.warn(String.format(
                        "\"%s\" fail due to no unknows host exception",
                        simpleWord));

            }
            if (word != null) {
                words.add(word);
            } else {
                nonFoundWord.add(simpleWord);
            }
            if ((count % 10 == 0) && (count != 0)) {
                logger.info(String.format("%f%% done...", (float) words.size()
                        / simpleWordList.size() * 100));
            }
        }

        /* deal with non-found words */
        int lastSize = 0;
        logger.debug(String.format("recheck %d words.", nonFoundWord.size()));
        while ((nonFoundWord.size() != 0) && (nonFoundWord.size() != lastSize)) {
            for (IWord simpleWord : nonFoundWord) {
                lastSize = nonFoundWord.size();
                try {
                    word = getWord(simpleWord.getWord());
                } catch (NoHttpResponseException e) {
                    try {
                        Thread.sleep(500);
                        word = getWord(simpleWord.getWord());
                    } catch (InterruptedException e1) {
                    } catch (NoHttpResponseException nhE) {
                        logger.warn(String.format(
                                "\"%s\" fail due to no http response",
                                simpleWord));
                    }

                } catch (UnknownHostException e) {
                    logger.warn(String.format(
                            "\"%s\" fail due to no unknows host exception",
                            simpleWord));

                }
                if (word != null) {
                    words.add(word);
                    nonFoundWord.remove(simpleWord);
                }
            }
        }

        logger.info(String.format(
                "%d words to parse, %d words successfully found.",
                simpleWordList.size(), words.size()));

        File tempFile = new File("contents/unparsedWords.bin");
        SerializeUtil.serialize(tempFile, nonFoundWord);
        logger.info(String.format("%d unparsed words write to file: %s",
                nonFoundWord.size(), tempFile));
        return words;
    }

    public IThesaurusWord getWord(String word) throws ClientProtocolException,
    IOException {
        IThesaurusWord wordInstance = new ThesaurusWord();
        wordInstance.setWord(word);

        // Prepare a request object
        HttpGet httpget = new HttpGet(MW_ROOT + MW_THEAURUS + word);
        // Execute the request
        HttpResponse response = httpclient.execute(httpget);
        // Examine the response status

        // Get hold of the response entity
        HttpEntity entity = response.getEntity();
        // If the response does not enclose an entity, there is no need
        // to worry about connection release
        Source source;

        IThesaurusItem[] items = null;
        if (entity == null) {
            logger.error(String.format("Entity is null while parse %s", word));
            return null;
        }
        InputStream instream = entity.getContent();
        try {
            source = new Source(instream);

            List<Element> explainItemsElementList = source
            .getAllElementsByClass("sense_content");

            if (explainItemsElementList.size() == 0) {
                logger.warn(String.format("\"%s\" - No entries found in %s",
                        word, MW_ROOT + MW_THEAURUS));
                return null;
            }
            logger
            .trace("============================================================");
            logger.debug(String.format(
                    "There are %d items in Thesaurus for word \"%s\"",
                    explainItemsElementList.size(), word));

            items = new ThesaurusItem[explainItemsElementList.size()];
            for (int i = 0; i < explainItemsElementList.size(); i++) {
                logger.trace(String.format("Parsing No.%d item.", i + 1));
                ThesaurusItem item = new ThesaurusItem();
                item.setIndex(i + 1);
                Element itemElement = explainItemsElementList.get(i);
                /* detal in one item */

                List<StartTag> tags = itemElement.getAllStartTags("span");

                /* get meaning */
                StringBuffer sb = new StringBuffer();
                for (int indexS = tags.get(0).getEnd(); indexS < tags.get(1)
                .getBegin(); indexS++) {
                    sb.append(source.charAt(indexS));
                }
                item.setMeaning(sb.toString());
                logger.trace(String.format("Meaning: %s", item.getMeaning()));

                /* get sentence */
                String s;
                List<Element> removeEs;

                List<StartTag> sentenceTags = itemElement
                .getAllStartTagsByClass("vi");
                String[] sentences = new String[sentenceTags.size()];
                for (int j = 0; j < sentenceTags.size(); j++) {
                    s = sentenceTags.get(j).getElement().getContent()
                    .toString();
                    removeEs = sentenceTags.get(j).getElement()
                    .getAllElements();
                    for (int index = 1; index < removeEs.size(); index++) {
                        s = s.replace(removeEs.get(index).toString(), removeEs
                                .get(index).getContent());
                    }
                    s = s.replace("&lt;", "");
                    s = s.replace("&gt;", "");
                    s = s.replace("&#x2BC;", "'");
                    s = s.replace("&#x2014;", "--");
                    s = s.replace("&#x201C", "\"");

                    sentences[j] = s;
                }
                item.setSentence(sentences);
                logger.trace(String.format("%d Sentences: %s",
                        sentences.length, Arrays.deepToString(sentences)));

                /*
                 * parse the word net : Synonyms/Related Words/Near Antonyms
                 * /Antonyms
                 */
                WordNet net;

                String[] wordList;
                for (StartTag tag : tags) {
                    // if (tag.getAttributeValue("class") == "vi") {
                    // /* sentense */
                    // s = tag.getElement().getContent().toString();
                    // removeEs = tag.getElement().getAllElements();
                    // for (int index = 1; index < removeEs.size(); index++)
                    // {
                    // s = s.replace(removeEs.get(index).toString(),
                    // "");
                    // }
                    //
                    //
                    // }
                    try {
                        net = WordNet.valueOf(tag.getAttributeValue("class"));

                        switch (net) {
                        case syn:
                            /* remove the extra nodes */
                            s = tag.getElement().getContent().toString();
                            removeEs = tag.getElement().getAllElements();
                            for (int index = 1; index < removeEs.size(); index++) {
                                s = s.replace(removeEs.get(index).toString(),
                                "");
                            }

                            wordList = (s.split(", *"));
                            item.setSyn(wordList);
                            logger.trace(String.format("%d synonyms: %s",
                                    wordList.length, Arrays
                                    .deepToString(wordList)));

                            break;
                        case rel:
                            s = tag.getElement().getContent().toString();
                            /* remove the extra nodes */
                            removeEs = tag.getElement().getAllElements();
                            for (int index = 1; index < removeEs.size(); index++) {
                                s = s.replace(removeEs.get(index).toString(),
                                "");
                            }
                            wordList = (s.split(", *"));
                            item.setRel(wordList);
                            logger.trace(String.format("%d related word: %s",
                                    wordList.length, Arrays
                                    .deepToString(wordList)));
                            break;
                        case near:
                            s = tag.getElement().getContent().toString();
                            /* remove the extra nodes */
                            removeEs = tag.getElement().getAllElements();
                            for (int index = 1; index < removeEs.size(); index++) {
                                s = s.replace(removeEs.get(index).toString(),
                                "");
                            }
                            wordList = (s.split(", *"));
                            item.setNear(wordList);
                            logger.trace(String.format("%d related word: %s",
                                    wordList.length, Arrays
                                    .deepToString(wordList)));
                            break;
                        case ant:
                            s = tag.getElement().getContent().toString();
                            /* remove the extra nodes */
                            removeEs = tag.getElement().getAllElements();
                            for (int index = 1; index < removeEs.size(); index++) {
                                s = s.replace(removeEs.get(index).toString(),
                                "");
                            }
                            wordList = (s.split(", *"));
                            item.setAnt(wordList);
                            logger.trace(String.format("%d related word: %s",
                                    wordList.length, Arrays
                                    .deepToString(wordList)));
                            break;
                        default:
                            logger
                            .warn("Should not happen !! switch to default");

                        }

                    } catch (IllegalArgumentException illArgE) {
                        logger.trace(String.format("Expected exception: %s",
                                illArgE.getMessage()));
                    }

                }
                items[i] = item;
            }

            // BufferedReader reader = new BufferedReader(
            // new InputStreamReader(instream));
            // // do something useful with the response
            // String line;
            // while ((line = reader.readLine()) != null) {
            // System.out.println(line);
            // }

        } catch (IOException ex) {
            // In case of an IOException the connection will be released
            // back to the connection manager automatically
            throw ex;
        } catch (RuntimeException ex) {
            // In case of an unexpected exception you may want to abort
            // the HTTP request in order to shut down the underlying
            // connection and release it back to the connection manager.
            httpget.abort();
            throw ex;
        } finally {
            // Closing the input stream will trigger connection release
            instream.close();
        }

        wordInstance.setThesaurusItems(items);
        return wordInstance;
    }

    public static WordThesaurusProcessor getInstance() {
        if (processor == null) {
            processor = new WordThesaurusProcessor();
        }
        return processor;
    }

    public void quickTest() throws ClientProtocolException, IOException {

        List<IWord> wordsToTest = new LinkedList<IWord>();
        wordsToTest.add(new SimplestWord("absolute"));
        wordsToTest.add(new SimplestWord("acute"));
        wordsToTest.add(new SimplestWord("expensive"));
        wordsToTest.add(new SimplestWord("mosquito"));
        WordThesaurusProcessor mw = WordThesaurusProcessor.getInstance();
        mw.getWords(wordsToTest);
        // logger.info("Test thesaurus in word \n"
        // + Arrays.deepToString());

    }

    public static void main(String args[]) throws ClientProtocolException,
    IOException {
        new WordThesaurusProcessor().quickTest();
    }

}
